package com.ctc.serialport;

import android.util.Log;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 串口工具类
 */
public class SerialPort
{

    /**
     * 加载动态链接库
     */
    static
    {
        System.loadLibrary("SerialPort");
    }

    private final String TAG = "SerialPort";

    /**
     * 文件描述
     * (注意：不要修改变量名，变量名与 C文件中的变量名对应，修改了之后会导致关闭串口时报错)
     */
    private FileDescriptor mFd;

    /**
     * 输入流（用于接收指令）
     */
    private FileInputStream mFileInputStream;

    /**
     * 输出流（用于发送指令）
     */
    private FileOutputStream mFileOutputStream;


    /**
     * 打开串口
     * 默认串口的配置（奇偶校验：无校验位，数据位：8，停止位：1，标记：0）
     *
     * @param device   串口设备
     * @param baudRate 波特率
     * @return 是否成功打开串口
     */
    public boolean openSerialPort(File device, int baudRate)
    {
        return openSerialPort(device, baudRate, 0, 8, 1, 0);
    }

    /**
     * 打开串口
     * 默认串口配置（标记：0）
     *
     * @param device   串口设备
     * @param baudRate 波特率
     * @param parity   奇偶校验（0-无校验位（默认：None），1-奇校验（Odd），2-偶校验（Even））
     * @param dataBits 数据位（5～8，默认：8）
     * @param stopBit  停止位（1或2，默认：1）
     * @return 是否成功打开串口
     */
    public boolean openSerialPort(File device, int baudRate, int parity, int dataBits, int stopBit)
    {
        return openSerialPort(device, baudRate, parity, dataBits, stopBit, 0);
    }

    /**
     * 打开串口
     *
     * @param device   串口设备
     * @param baudRate 波特率
     * @param parity   奇偶校验（0-无校验位（默认：None），1-奇校验（Odd），2-偶校验（Even））
     * @param dataBits 数据位（5～8，默认：8）
     * @param stopBit  停止位（1或2，默认：1）
     * @param flags    标记（默认：0）
     * @return 是否成功打开串口
     */
    public boolean openSerialPort(File device, int baudRate, int parity, int dataBits, int stopBit, int flags)
    {
        if (null == device || !device.exists())
        {
            Log.w(TAG, "openSerialPort() ---> 串口不存在");
            return false;
        }

        Log.d(TAG, String.format("openSerialPort() ---> 串口地址: %s   波特率: %s   奇偶校验: %s   数据位: %s   停止位: %s   标记: %s", device.getAbsolutePath(), baudRate, parity, dataBits, stopBit, flags));

        //打开新的串口需要先关闭旧串口
        closeSerialPort();

        try
        {
            // 获取ROOT权限
            Process su = Runtime.getRuntime().exec("su");
            // 修改文件属性为 [可读 可写 可执行]
            String cmd = "chmod 777 " + device.getAbsolutePath() + "\nexit\n";
            su.getOutputStream().write(cmd.getBytes());
            su.getOutputStream().flush();
            if (0 == su.waitFor() && device.canRead() && device.canWrite() && device.canExecute())
            {
                mFd = open(device.getAbsolutePath(), baudRate, parity, dataBits, stopBit, flags);
                if (mFd == null)
                {
                    Log.w(TAG, String.format("openSerialPort() ---> 串口: %s 打开失败", device.getAbsolutePath()));
                }
                else
                {
                    mFileInputStream = new FileInputStream(mFd);
                    mFileOutputStream = new FileOutputStream(mFd);
                    Log.i(TAG, String.format("openSerialPort() ---> 串口: %s 打开成功", device.getAbsolutePath()));
                    return true;
                }
            }
            else
            {
                Log.w(TAG, String.format("openSerialPort() ---> 串口: %s 打开失败，Error: 没有 root权限", device.getAbsolutePath()));
            }
        }
        catch (Exception e)
        {
            Log.w(TAG, String.format("openSerialPort() ---> 串口: %s 打开失败，Error: %s", device.getAbsolutePath(), e.toString()));
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 关闭串口
     */
    public boolean closeSerialPort()
    {
        Log.d(TAG, "closeSerialPort() ---> 关闭串口");

        if (mFileInputStream != null)
        {
            try
            {
                mFileInputStream.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            mFileInputStream = null;
        }

        if (mFileOutputStream != null)
        {
            try
            {
                mFileOutputStream.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            mFileOutputStream = null;
        }

        if (mFd != null)
        {
            close();
            mFd = null;
        }

        return true;
    }

    /**
     * 获取输入流
     * （用于接收串口数据）
     *
     * @return 输入流
     */
    public InputStream getInputStream()
    {
        return mFileInputStream;
    }

    /**
     * 获取输出流
     * （用于发送串口数据）
     *
     * @return 输出流
     */
    public OutputStream getOutputStream()
    {
        return mFileOutputStream;
    }

    /**
     * 串口是否已打开
     *
     * @return 是否已打开
     */
    public boolean isOpen()
    {
        return mFileInputStream != null && mFileOutputStream != null;
    }

    /**
     * 打开串口
     * （跟硬件通讯的方法）
     *
     * @param path     串口地址
     * @param baudRate 波特率
     * @param parity   奇偶校验（0-无校验位（默认：None），1-奇校验(Odd)，2-偶校验(Even)）
     * @param dataBits 数据位（5～8，默认：8）
     * @param stopBit  停止位（1或2，默认：1）
     * @param flags    标记（默认：0）
     */
    private native FileDescriptor open(String path, int baudRate, int parity, int dataBits, int stopBit, int flags);

    /**
     * 关闭串口
     * （跟硬件通讯的本地方法）
     */
    private native void close();
}
